(* Creme Caramel Compiler (Triple C) - Michele Caceffo - 2008 *)

open Exceptions;;
open Syntaxtree;;
open Print_syntaxtree;;
open Intermediate;;
open Declaration;;
open Expressions;;

(** Contains the function to print the intermediate code into an output channel *)

(** Prints on the provided channel the selected instruction type
    @param inst The instruction code
    @param out The output stream
*)
let print_instcode (inst: inst_type) (out: out_channel) = match inst with 
          CPY       ->  output_string out "CPY   "
        | CPY2PT    ->  output_string out "CPY2PT"
	| SETNULL   ->  output_string out "SETNULL"
        | AGET      ->  output_string out "AGET  "
        | ASET      ->  output_string out "ASET  "
        | ADD       ->  output_string out "ADD   "
        | SUB       ->  output_string out "SUB   "
        | MUL       ->  output_string out "MUL   "
        | DIV       ->  output_string out "DIV   "
        | AND       ->  output_string out "AND   "
        | OR        ->  output_string out "OR    "
        | NOT       ->  output_string out "NOT   "
        | CEQU      ->  output_string out "CEQU  "
        | CNE       ->  output_string out "CNE   "
        | CG        ->  output_string out "CG    "
        | CGE       ->  output_string out "CGE   "
	| REF       ->  output_string out "REF   " (* LDL: print the REF label *)
	| DEREF     ->  output_string out "DEREF " (* LDL: print the DEREF label *)
        | JNE       ->  output_string out "JNE   "
        | GOTO      ->  output_string out "GOTO  "
        | PARAM     ->  output_string out "PARAM "
        | CALL      ->  output_string out "CALL  "
        | RET       ->  output_string out "RET   "
        | NOP       ->  output_string out "NOP   "
        | HALT      ->  output_string out "HALT  "
        | OUT       ->  output_string out "OUT   "

(** Converts a value into a string
    @param v A value
    @return The string conversion of the value
*)
let string_of_value (v: value) = match v with
          I(n)   ->  "INT: "^(string_of_int n)
        | F(r)   ->  "REAL: "^(string_of_float r)
        | O(p)   ->  "OFFSET: "^(string_of_offset p)
	| ONull  ->  "NULL"

(** Prints on the provided channel the selected element 
    @param e The element to print
    @param out The output channel
*)
(* LDL: update the print_element function in order to consider the introduction of pointer type values into the code structures *)
let print_element (e: element) (out: out_channel) = match e with
          Reg(r,DInt)             ->  output_string out ("\treg["^(string_of_int r)^"].i")
        | Reg(r,DFloat)           ->  output_string out ("\treg["^(string_of_int r)^"].f")
        | Reg(r,DPointer(_))      ->  output_string out ("\treg["^(string_of_int r)^"].p")
        | Off(o,DInt,_)           ->  output_string out ("\toffset "^(string_of_int o))
        | Off(o,DFloat,_)         ->  output_string out ("\toffset "^(string_of_int o))
        | Off(o,DPointer(_),_)    ->  output_string out ("\toffset "^(string_of_int o))
	| Off(o,DRecord(_),_)     ->  output_string out ("\toffset "^(string_of_int o))
        | Label(l)                ->  output_string out ("\tLabel nr. "^(string_of_int l))
        | Val(v)                  ->  output_string out ("\tVal "^(string_of_value v))
        | Subr(s)                 ->  output_string out ("\tSubr "^s)
        | Null                    ->  output_string out "\tNULL\t"
        | _                       ->  raise (CONFIRM_RULE "This happens when a register or an offset has type bool, which means never")

(** Takes an instruction and print it our into the provided channel
    @param inst The instruction code to print
    @param e1 The first element of the instruction
    @param e2 The second element of the instruction
    @param e3 The third element of the instruction
    @param out The output channel
*)
let print_instruction ((inst, e1, e2, e3): instruction) (out: out_channel) = (
        print_instcode inst out;
        print_element e1 out;
        print_element e2 out;
        print_element e3 out;
        output_string out "\n"
        )

(** Prints the list of instructions
    @param out The output channel
    @param ic The instruction list
*)
let rec print_ICode (out: out_channel) (ic: instruction list) = match ic with
        []      ->  ()
      | i::is   ->  print_instruction i out; print_ICode out is

(** Prints one bind of an Hashtable
    @param out The output channel
    @param Ide(l) The table key
    @param a The first element of the value binded with the ide into the table
    @param b The second element of the value binded with the ide into the table
    @param c The third element of the value binded with the ide into the table
*)
let dumpDecTable (out: out_channel) (Ide(l):ide) ((a,b,c): (int * int * element)) = 
        output_string out ((l)^"\t"^(string_of_int a)^"\t"^(string_of_int b)^"\t->\t");
        (print_element c out);
        output_string out ("\n")

(** Calls the dumpDecTable for all the declarations table
    @param out The output channel
    @param dect The declarations table
*)
let print_DecTable (out: out_channel) (dect: dec_table) = (
        output_string (out) "\n/*\n";
        Hashtbl.iter (dumpDecTable out) dect;
        output_string (out) "*/\n"
)
